/*
 * CHttpServer.h
 *
 *  Created on: 2016-7-25
 *      Author: terry
 */

#ifndef CHTTPSERVER_H_
#define CHTTPSERVER_H_

#include "BasicType.h"
#include "HttpServer.h"
#include "TThread.h"
#include <string>
#include <map>
#include <vector>
#include <deque>
#include <time.h>
#include <mutex>

#include "TMap.h"

#include "HttpAfx.h"


class HttpTimerTask
{
public:

	virtual ~HttpTimerTask() {}

	virtual bool open(int seconds, HttpServer::Task task) =0;

	virtual void close() =0;

	virtual int poll() = 0;

};


class CHttpServer: public HttpServer, protected comn::Thread
{
public:
	static const int ASYNC_DURATION = 60;

public:
	CHttpServer();
	virtual ~CHttpServer();


	virtual bool setWebRoot(const char* dirPath);

	virtual bool setUploadDir(const char* url, const char* dirPath, UploadHandler handler);

	virtual bool setUserFile(const char* filePath);

	virtual bool addDir(const char* url, const char* dirPath, HttpAuth auth = HttpAuth());
	
	virtual bool addApi(const char* prefix);

	virtual bool start(const char* ip, int port, HttpHandler* defHandler);

	virtual void stop();

	virtual bool isStarted();


    virtual bool sendResponse(HttpConnection* conn, const HttpResult& result);

    virtual bool send(HttpConnection* conn, const void* data, size_t len);

	virtual bool enableSsl(int port, const char* caPath, const char* certPath, const char* keyPath);

	virtual bool getRemoteAddr(HttpConnection* conn, std::string& name);

	virtual bool setUploadSubDir(const char* subUrl, const char* dirPath);

	virtual void broadcast(WsDataType type, const char* data, size_t len);

	virtual void setWebSocket(const char* url, Handler handler, HttpAuth auth);

	virtual bool sendFrame(HttpConnection* conn, WsDataType type, const char* data, size_t len);

	virtual void setVirtualFile(const char* url, Handler handler);

	virtual void setTimerTask(int seconds, Task task);


public:
	void handleHttpEvent(struct mg_connection *nc, int ev, struct http_message *hm);

	void handleUpload(struct mg_connection *nc, int ev, void *ev_data);

	void handleDirEntry(struct mg_connection *nc, int ev, void *ev_data);

	static void sendApiResponse(mg_connection *nc, const HttpResult& result);

	static void sendJsonResponse(mg_connection *nc, int code, const char *reason, const std::string& json);
	static void sendJsonResponse(mg_connection *nc, int code, const char *reason, const char* json);

	static void sendResponse(mg_connection *nc, int code, const char *reason,
		const std::string& origin,
		const std::string& mime,
		const char* content);

	static void sendResponse(mg_connection *nc, int code, const char *reason,
		const std::string& origin,
		const std::string& mime,
		const std::string& content);

    static const char* getCodeName(int code);

public:
	static void ev_handler(struct mg_connection *nc, int ev, void *ev_data);

protected:
	void apiHandler(struct mg_connection *nc, int ev, void *ev_data);

	void handleClose(struct mg_connection *nc);
	
protected:
	virtual int run();
	virtual void doStop();

protected:
	void handleApi(struct mg_connection* nc, struct http_message *hm, mg_str& uri);
	void handleFileApi(struct mg_connection* nc, struct http_message* hm, mg_str& uri);

	std::string	makeUploadPath(const std::string& uri, const std::string& query, const std::string& filename);
    std::string makeUploadName(const char* filename);

	bool setupUser();

	std::string makeUser(const std::string& name, const std::string& realm, const std::string& passwd);

	bool resetUser(const std::string& name, const std::string& passwd);


	struct DirEntry
	{
		std::string url;
		std::string	dirPath;
		mg_serve_http_opts opts;
		HttpAuth auth;

		DirEntry():
			opts()
		{
		}
	};

	typedef std::map< std::string, DirEntry>	DirEntryMap;

	bool findDirEntry(const char* url, DirEntry& entry);
	bool findDirEntry(struct mg_str& url, DirEntry& entry);
	DirEntry* findDirEntry(const char* url);

	typedef std::map< std::string, std::string>	PrefixMap;
	bool findPrefix(const struct mg_str& url, std::string& prefix);


    int convertStatusCode(int rc);

	bool isXml(const std::string& str);

public:
	typedef comn::Map< mg_connection*, mg_connection*>  ConnectionMap;

    struct AsyncRecord
    {
        mg_connection* connection;
        HttpResult result;
		int dataType;

        AsyncRecord():
            connection(),
			dataType(),
            result()
        {
        }
    };

    typedef std::deque< AsyncRecord >     AsyncRecordDeque;


    bool fetchAsyncRecord(AsyncRecord& record);
    void signalAsyncResult();

    bool putAsyncRecord(AsyncRecord& record);

    void clearAsyncRecord(mg_connection* connection);
    void clearAsyncRecord();

    void handleAsyncRecord(AsyncRecord& record);


    virtual void postEvent();
    virtual bool timedwait(int ms);

	bool findUploadDir(const std::string& url, std::string& dirpath);

protected:
	HttpHandler*	m_handler;

	std::string	    m_webRoot;

	std::string     m_uploadUrl;
	std::string     m_uploadDir;
	UploadHandler   m_uploadHandler;

	std::string     m_userFile;

	struct mg_mgr	m_mgr;
	mg_serve_http_opts	m_opts;
	struct mg_connection*	m_listenConnection;

	DirEntryMap	m_entryMap;
	PrefixMap	m_prefixMap;

	std::mutex  m_mutex;

	std::string	m_sslPort;
	struct mg_connection*	m_sslConnection;

	ConnectionMap   m_connections;

	AsyncRecordDeque    m_asyncRecords;

	std::atomic_bool    m_polling;

	PrefixMap	m_uploadSubDirMap;

	std::string	m_wsUrl;
	Handler		m_wsHandler;
	HttpAuth	m_wsAuth;

	std::string	m_vfUrl;
	Handler		m_vfHandler;

	HttpTimerTask* m_timerTask;

};

#endif /* CHTTPSERVER_H_ */
